﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;
using eCRAM.Model;

namespace eCRAM.WebSite.Tools
{
    public static class Helpers
    {
        public static MvcHtmlString GetTextboxTotal<T>(this HtmlHelper<T> helper, object value)
        {

            return helper.TextBox("", value, new { style = "border: 0px; width:90px", @readonly = "readonly" });
        }

        

        /// <summary>
        /// Affiche un lien hypertexte
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="id"></param>
        /// <param name="visibility"></param>
        /// <param name="actionName"></param>
        /// <param name="linkText"></param>
        /// <param name="formName"></param>
        /// <param name="additionalScript"></param>
        /// <returns></returns>
        public static MvcHtmlString HyperLink(this HtmlHelper helper, string id, string visibility, string actionName, string linkText, string formName, object additionalScript)
        {
            string actionVal = UrlHelper.GenerateUrl(null, actionName, null, null, helper.RouteCollection, helper.ViewContext.RequestContext, false);
            return MvcHtmlString.Create(string.Format("<a id=\"{2}\" style=\"visibility : {3}\" onclick=\"{1}\" class=\"PseudoLink\">{0}</a>",
                linkText,
                string.Format(" var form = document.forms['{2}']; form.action='{0}'; {1}; form.submit();", actionVal, additionalScript, formName), id, visibility));
        }


        /// <summary>
        /// Affiche une dropdownlist qui valide le formulaire lors du changement de sélection
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="expression"></param>
        /// <param name="items"></param>
        /// <param name="formName"></param>
        /// <param name="onChangeScript"></param>
        /// <param name="actionName"></param>
        /// <returns></returns>
        public static MvcHtmlString DropDownListWithSubmitOnChange<T, Y>(this HtmlHelper<T> helper, string actionName, Expression<Func<T, Y>> expression, IEnumerable<SelectListItem> items, string formName, string onChangeScript)
        {
            return DropDownListWithSubmitOnChange(helper, actionName, expression, items, formName, onChangeScript, false);
        }

        /// <summary>
        /// Affiche une dropdownlist qui valide le formulaire lors du changement de sélection
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="expression"></param>
        /// <param name="items"></param>
        /// <param name="formName"></param>
        /// <param name="onChangeScript"></param>
        /// <param name="actionName"></param>
        /// <param name="isReadOnly"></param>
        /// <returns></returns>
        public static MvcHtmlString DropDownListWithSubmitOnChange<T, Y>(this HtmlHelper<T> helper, string actionName, Expression<Func<T, Y>> expression, IEnumerable<SelectListItem> items, string formName, string onChangeScript, bool isReadOnly)
        {
            if(isReadOnly)
            {
                return helper.ReadOnlyDropDownListFor(expression, items, true);
            }
            var actionVal = UrlHelper.GenerateUrl(null, actionName, null, null, helper.RouteCollection, helper.ViewContext.RequestContext, false);
            return helper.DropDownListFor(expression, items,
                new { onchange = string.Format(@"var form = document.forms['{0}']; form.action='{2}'; {1}; form.submit();", formName, onChangeScript, actionVal) });

        }



        //public static MvcHtmlString TextBox(this HtmlHelper helper , string Id , string visibility , object additionnalScript)
        //{
        //    return MvcHtmlString.Create(helper.TextBox())
        //}

        /// <summary>
        /// Affiche un text box en lecture seule
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="helper"></param>
        /// <param name="target"></param>
        /// <param name="valueList"></param>
        /// <param name="isReadOnly"></param>
        /// <returns></returns>
        public static MvcHtmlString ReadOnlyDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> target, IEnumerable<SelectListItem> valueList, bool isReadOnly)
        {
            if (isReadOnly)
            {
                var propertyName = GetPropertyPath(target);
                return MvcHtmlString.Create(
                    helper.DropDownList(propertyName, valueList, new { disabled = "\"disabled\"" })
                    + helper.HiddenFor(target).ToString());
            }
            return helper.DropDownListFor(target, valueList);
        }


        /// <summary>
        /// Affiche un text box en lecture seule
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="name"></param>
        /// <param name="valueList"></param>
        /// <param name="selectValue"></param>
        /// <param name="isReadOnly"></param>
        /// <returns></returns>
        public static MvcHtmlString ReadOnlyDropDownList(this HtmlHelper helper, string name, SelectList valueList, string selectValue, bool isReadOnly)
        {
            if (isReadOnly)
            {

                //string propertyName = GetPropertyPath(target);)
                return MvcHtmlString.Create(helper.Encode(selectValue));
                //helper.DropDownList(name, ValueList, new { disabled = "\"disabled\"" }).ToString());
            }

            return helper.DropDownList(name, valueList);

        }





        /// <summary>
        /// Ecrit un ensemble de chaines dans le buffer de sortie
        /// </summary>
        /// <param name="response"></param>
        /// <param name="contents"></param>
        public static void WriteMultiples(this HttpResponse response, params MvcHtmlString[] contents)
        {
            foreach (var s in contents)
            {
                response.Write(s);

            }
        }

        /// <summary>
        /// Ecrit un ensemble de chaines dans le buffer de sortie
        /// </summary>
        /// <param name="response"></param>
        /// <param name="contents"></param>
        public static void WriteMultiples(this HttpResponse response, params object[] contents)
        {
            foreach (var s in contents)
            {
                response.Write(s);
            }
        }



        /// <summary>
        /// Affiche un text box en lecture seule
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="helper"></param>
        /// <param name="target"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static MvcHtmlString ReadOnlyTextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> target, string value)
        {
            return MvcHtmlString.Create(
                helper.TextBox("txtBox", value, new { disabled = "\"disabled\"" })
                + helper.HiddenFor(target).ToString());
        }

        /// <summary>
        /// Génére un champ d'affichage / saisie d'une date
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="helper"></param>
        /// <param name="model"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        public static MvcHtmlString DateBoxFor<TEntity>(this HtmlHelper<TEntity> helper, TEntity model, Expression<Func<TEntity, DateTime?>> property)
        {
            DateTime? date = property.Compile().Invoke(model);
            // Here you can format value as you wish             
            var value = date.HasValue ? date.Value.ToShortDateString() : string.Empty;
            var name = GetPropertyPath(property);

            return MvcHtmlString.Create(
                helper.HiddenFor(property)
                + ""
                + helper.TextBox("tmp_" + name, value, new { onblur = string.Format("{0}.value=tmp_{0}.value;", name) }));

        }

        /// <summary>
        /// Génére un champ d'affichage / saisie d'une date
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="helper"></param>
        /// <param name="model"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        public static MvcHtmlString DateBoxFor<TEntity>(this HtmlHelper<TEntity> helper, TEntity model, Expression<Func<TEntity, DateTime>> property)
        {
            DateTime date = property.Compile().Invoke(model);
            // Here you can format value as you wish             
            var value = date.ToShortDateString();
            var name = GetPropertyPath(property);

            return MvcHtmlString.Create(
                    helper.HiddenFor(property)
                    + ""
                    + helper.TextBox("tmp_" + name, value, new { onblur = string.Format("{0}.value=tmp_{0}.value;", name) }));

        }

        public static MvcHtmlString TableForItems<TModel, TItemType>(this HtmlHelper<TModel> helper, Expression<Func<TModel, IEnumerable<TItemType>>> property, TableDescription<TItemType> description, IEnumerable<TItemType> items, string formName)
            where TModel : class
            where TItemType : class
        {
            var name = GetPropertyPath(property);

            var sb = new StringBuilder();
            var sbHiddenFields = new StringBuilder();
            sb.Append("<table>");

            // ajout du header
            sb.Append("<tr>");
            foreach (var row in description.Rows)
            {
                sb.AppendFormat(@"<th>{0}</th>", row.Header);
            }
            sb.Append(@"</tr>");

            int i = 0;
            // Ajout des lignes de données
            foreach (TItemType item in items)
            {
                // On génére la collection de champs hidden pour un objet
                //sbHiddenFields.Append(
                //    helper.HiddenFieldsForValueObject(string.Format("{0}[{1}]", name, i), item));


                if (!(item is IStatusableObject) || (item as IStatusableObject).Status != ObjectStatus.Deleted)
                {
                    sb.Append("<tr>");
                    foreach (var row in description.Rows)
                    {
                        sb.AppendFormat(@"<td>{0}</td>", row.GetValue(helper, item, formName, string.Format("{0}[{1}]", name, i), i));
                    }

                    // ajout des liens de suppression éditions
                    if (description.IsEditable || description.IsDeletable)
                    {
                        var additionalScript = string.Empty;
                        if (description.GetKeyMethod != null)
                        {
                            string key = description.GetKeyMethod.Invoke(item).ToString();
                            additionalScript = string.Format("form['{0}'].value = '{1}'", description.HiddenFieldName, key);
                        }

                        sb.Append("<td>");
                        if (description.IsEditable)
                        {
                            sb.Append(helper.Image(description.EditActionName, "~/Content/Images/edit.png","Modifier", formName, additionalScript));
                            //sb.Append(helper.HyperLink(description.EditActionName, "Modifier", formName,
                            //   additionalScript));
                        }

                        if (description.IsDeletable)
                        {
                            sb.Append(helper.Image(description.DeleteActionName, "~/Content/Images/delete.png", "Supprimer", formName, additionalScript));

                            //sb.Append(helper.HyperLink(description.DeleteActionName, "Supprimer", formName, additionalScript));
                        }
                    }

                    sb.Append(@"</td>");
                    sb.Append(@"</tr>");
                }
                i++;
            }


            sb.Append(@"</table>");
            return MvcHtmlString.Create(
                sbHiddenFields.ToString()
                + helper.Hidden(description.HiddenFieldName)
                + sb);
        }




        /// <summary>
        /// Génére un ensemble de champs hidden pour stocker une collection de ValueObject
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="helper"></param>
        /// <param name="propertyName"></param>
        /// <param name="collection"></param>
        /// <returns></returns>
        public static MvcHtmlString HiddenFieldsForValueObjectCollection<T>(this HtmlHelper helper, string propertyName, IEnumerable<T> collection) where T : class
        {
            if (collection == null)
            {
                return MvcHtmlString.Empty;
            }
            var sb = new StringBuilder();
            int i = 0;
            foreach (T item in collection)
            {
                sb.Append(
                    helper.HiddenFieldsForValueObject(string.Format("{0}[{1}]", propertyName, i), item));
                i++;
            }
            return MvcHtmlString.Create(sb.ToString());
        }

        /// <summary>
        /// Génére un ensemble de champ hidden pour stocker un ValueObject
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="helper"></param>
        /// <param name="propertyName"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public static MvcHtmlString HiddenFieldsForValueObject<T>(this HtmlHelper helper, string propertyName, T item) where T : class
        {
            if (item == null)
            {
                return MvcHtmlString.Empty;
            }

            var sb = new StringBuilder();
            var listProperties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.Public);
            foreach (var property in listProperties)
            {
                object val = property.GetGetMethod().Invoke(item, new object[] { });

                string sVal = string.Empty;
                if (val != null)
                {
                    sVal = val.ToString();
                }

                //sb.Append(helper.Hidden(string.Format("{0}.{1}", PropertyName, Property.Name), val));
                var builder = new TagBuilder("input");
                builder.MergeAttribute("name", string.Format("{0}.{1}", propertyName, property.Name));
                builder.MergeAttribute("value", sVal);
                builder.MergeAttribute("type", "hidden");
                builder.GenerateId(string.Format("{0}.{1}", propertyName, property.Name));

                sb.Append(builder);

            }

            return MvcHtmlString.Create(sb.ToString());
        }

        /// <summary>
        /// Obtient le nom d'une propriéte à partir d'une lambda expression
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="property"></param>
        /// <returns></returns>
        internal static string GetPropertyPath<TEntity, TProperty>(Expression<Func<TEntity, TProperty>> property)
        {
            var match = Regex.Match(property.ToString(), @"^[^\.]+\.([^\(\)]+)$");
            return match.Groups[1].Value;
        }

        /// <summary>
        /// Affiche un lien hypertexte
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <param name="helper"></param>
        /// <param name="actionName"></param>
        /// <param name="linkText"></param>
        /// <param name="formName"></param>
        /// <returns></returns>
        public static MvcHtmlString HyperLink<TModel>(this HtmlHelper<TModel> helper, string actionName, string linkText, string formName)
        {
            return HyperLink(helper, actionName, linkText, formName, null);
        }

        /// <summary>
        /// Affiche un lien hypertexte
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <param name="helper"></param>
        /// <param name="actionName"></param>
        /// <param name="linkText"></param>
        /// <param name="formName"></param>
        /// <param name="additionalScript"></param>
        /// <returns></returns>
        public static MvcHtmlString HyperLink<TModel>(this HtmlHelper<TModel> helper, string actionName, string linkText, string formName, object additionalScript)
        {
            return (helper as HtmlHelper).HyperLink(actionName, linkText, formName, additionalScript);
        }

        /// <summary>
        /// Affiche un lien hypertexte
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="actionName"></param>
        /// <param name="linkText"></param>
        /// <param name="formName"></param>
        /// <returns></returns>
        public static MvcHtmlString HyperLink(this HtmlHelper helper, string actionName, string linkText, string formName)
        {
            return HyperLink(helper, actionName, linkText, formName, null);
        }

        /// <summary>
        /// Affiche un lien hypertexte
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="actionName"></param>
        /// <param name="linkText"></param>
        /// <param name="formName"></param>
        /// <param name="additionalScript"></param>
        /// <returns></returns>
        public static MvcHtmlString HyperLink(this HtmlHelper helper, string actionName, string linkText, string formName, object additionalScript)
        {
            var actionVal = UrlHelper.GenerateUrl(null, actionName, null, null, helper.RouteCollection, helper.ViewContext.RequestContext, false);
            return MvcHtmlString.Create(string.Format("<a onclick=\"{1}\" class=\"PseudoLink\">{0}</a>",
                linkText,
                string.Format(" var form = document.forms['{2}']; form.action='{0}'; {1}; form.submit();", actionVal, additionalScript, formName)));
        }

        /// <summary>
        /// Génére une image clickable qui déclenche une validation du formulaire
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="actionName"></param>
        /// <param name="url"></param>
        /// <param name="tooltip"></param>
        /// <param name="formName"></param>
        /// <param name="additionalScript"></param>
        /// <returns></returns>
        public static MvcHtmlString Image(this HtmlHelper helper, string actionName, string url, string tooltip, string formName, object additionalScript)
        {
            return helper.Image(actionName, url, tooltip, formName, additionalScript, "Icon");
        }

        /// <summary>
        /// Génére une image clickable qui déclenche une validation du formulaire
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="actionName"></param>
        /// <param name="url"></param>
        /// <param name="tooltip"></param>
        /// <param name="formName"></param>
        /// <param name="additionalScript"></param>
        /// <param name="className"></param>
        /// <returns></returns>
        public static MvcHtmlString Image(this HtmlHelper helper, string actionName, string url, string tooltip, string formName, object additionalScript, string className)
        {
            var actionVal = UrlHelper.GenerateUrl(null, actionName, null, null, helper.RouteCollection, helper.ViewContext.RequestContext, false);
            
            return MvcHtmlString.Create(string.Format("<img id='{2}' src=\"{0}\" onclick=\"{1}\" class=\"{4}\" title=\"{3}\" alt=\"{3}\" />", 
                helper.MapPath(url),
                string.Format(" var form = document.forms['{2}']; form.action='{0}'; {1}; form.submit();", actionVal, additionalScript, formName), 
                actionName, tooltip, className));
        }

        /// <summary>
        /// Convertit un chemin relatif à la racine du serveur en chemin compatible HTML
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string MapPath(this HtmlHelper helper, string url)
        {
            if (!url.StartsWith("~"))
            {
                return url;
            }
            if (helper.ViewContext != null && helper.ViewContext.HttpContext != null && helper.ViewContext.HttpContext.Request.PhysicalApplicationPath != null)
            {
                return string.Format(
                    "\\{0}",
                    helper.ViewContext.HttpContext.Request.MapPath(url).Replace(
                        helper.ViewContext.HttpContext.Request.PhysicalApplicationPath, ""))
                 .Replace("\\", "/");
            }
            return url;
        }

        /// <summary>
        /// Créer un composant Checkbox pour les booléens nullables
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <param name="helper"></param>
        /// <param name="property"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        public static MvcHtmlString CheckBoxForNullableBool<TModel>(this HtmlHelper helper, Expression<Func<TModel, bool?>> property, TModel model)
        {
            string name = GetPropertyPath(property);
            bool isChecked = property.Compile().Invoke(model) ?? false;
            return helper.CheckBox(name, isChecked);
        }
    }
}